/* Original Author: Dana Hanna (ideas@anappaday.com)
** Modified by: Nolan B. Smith (nbsmith@gmail.com)
** Last Modified: Oct 08 2006
** Form1
** 
*/

/* Changelog
** Better ls functionality
** Added clr
** jediopacity no longer returns unknown command
** jediopacity will return a message if not properly invoked
** ignores tabs altogether, may try for autocomplete in the future but thats a big maybe
** 
*/

/* Changelog 2
** clear acts like clr - for unix addicts
** new system of interpreting commands (no shitty "if else")
** something a'la unix prompt
*/


/* TODO
** font-weight command
** !!Focus
** same shortcut handling - like commands
*/
using System;
using System.IO;
using System.Threading;
using System.Diagnostics;
using System.Collections.Generic;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using AnAppADay.JediConsole.KeyHook;
using System.Text.RegularExpressions;

namespace AnAppADay.JediConsole.WinApp
{
    public partial class Form1 : Form
    {

        private string _cmd;
        private string _args;
        private StringBuilder _curLine = new StringBuilder();
        private ArrayList _commandBuffer = new ArrayList();
        private int _commandBufferPosition = 1;
        private int _inputEliminateChars = 0;
        Process _process;
        Thread _readOutThread;
        Thread _readErrThread;

		string _user_name;
		string _host_name;
		Regex _reg_prompt = new Regex("[A-Z]:.*>");

		private Hashtable actions;
		

        delegate void SingleStringInvoker(string arg);
		delegate void Command(string[] args);



        public event EventHandler Exiting;

        public Form1(string cmd, string args, double opacity)
        {
            InitializeComponent();
            
            SetLocation();

            _cmd = cmd;
            _args = args;

			#region Action and related messages
			actions = new Hashtable();

			actions.Add("ls", new Command(CommandLs));
			actions.Add("clr", new Command(CommandClearScreen));
			actions.Add("cls", new Command(CommandClearScreen));
			actions.Add("clear", new Command(CommandClearScreen));//unix way :D
			actions.Add("exit", new Command(CommandExit));
			actions.Add("logout", new Command(CommandExit));
			actions.Add("jediopacity", new Command(CommandJediOpacity));
			actions.Add("pwd", new Command(CommandPwd));

			#endregion

			string [] tmp = System.Security.Principal.WindowsIdentity.GetCurrent().Name.ToString().Split('\\');
			_host_name = tmp[0];
			_user_name = tmp[1];

            Opacity = opacity;
        }

        private void SetLocation()
        {
            Point p = new Point();
            p.X = Screen.PrimaryScreen.WorkingArea.Width / 2 - Width / 2;
            p.Y = 0;
            Location = p;
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            ProcessStartInfo si = new ProcessStartInfo(_cmd, _args);
            si.CreateNoWindow = true;
            si.RedirectStandardError = true;
            si.RedirectStandardInput = true;
            si.RedirectStandardOutput = true;
            si.UseShellExecute = false;
            si.WindowStyle = ProcessWindowStyle.Hidden;

            _process = new Process();
            _process.StartInfo = si;
            _process.Start();

            _readOutThread = new Thread(new ParameterizedThreadStart(ReadOutStream));
            _readOutThread.Start(_process.StandardOutput);
            _readErrThread = new Thread(new ParameterizedThreadStart(ReadErrStream));
            _readErrThread.Start(_process.StandardError);
        }

        void ReadOutStream(object oStream)
        {
            StreamReader read = oStream as StreamReader;
            char[] buffer = new char[1024];
            while (true)
            {
                int cnt = read.Read(buffer, 0, buffer.Length);
                lock (this)
                {
                    int charsToRemove = 0;
                    if (_inputEliminateChars > 0)
                    {
                        if (_inputEliminateChars > cnt)
                        {
                            _inputEliminateChars -= cnt;
                            return;
                        }
                        else
                        {
                            charsToRemove = _inputEliminateChars;
                            cnt -= charsToRemove;
                            _inputEliminateChars = 0;
                            char[] buffer2 = new char[buffer.Length];
                            Array.Copy(buffer, charsToRemove, buffer2, 0, cnt);
                            buffer = buffer2;
                        }
                    }
                    string message = new string( buffer, 0, cnt );
                    //my attempt at fixing ls
                    //seems to work and doesn't interfere with other things
                    message = message.Replace( "\n", "\r\n" );

					//something a'la unix prompt :D
					//user@host: work_dir $

					if (_reg_prompt.Match(message).Success)
					{
						string prompt = "";
						string active_dir = "";
						int dir = message.LastIndexOf('\\');

						active_dir = message.Remove(0, dir + 1);

						prompt = "[" + _user_name + "@" + _host_name + ": " + active_dir;
						prompt =  prompt.Replace(">", "]$ ") ;

						message = _reg_prompt.Replace(message, prompt);
					}

                    textBox1.Invoke(new SingleStringInvoker(textBox1.AppendText), message);
                }
            }
        }

        void ReadErrStream(object oStream)
        {
            StreamReader read = oStream as StreamReader;
            char[] buffer = new char[1024];
            while (true)
            {
                int cnt = read.Read(buffer, 0, buffer.Length);
                textBox1.Invoke(new SingleStringInvoker(textBox1.AppendText),
                                new string(buffer, 0, cnt));
            }
        }

        private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == '\r')
            {
                e.Handled = true;

				ParseCommand(_curLine.ToString());	//this way we don't have this shitty "if else" thing

                if (_commandBufferPosition < 1)
                    _commandBufferPosition = 1;
                _commandBuffer.Insert(_commandBufferPosition-1, _curLine.ToString());
                _commandBufferPosition++;

                _inputEliminateChars = _curLine.Length;

                _curLine.Append(Environment.NewLine);
                _process.StandardInput.Write(_curLine.ToString());

                _curLine = new StringBuilder();
            }
            else if (e.KeyChar == '\b')
            {
                MoveToEnd();
                if (_curLine.Length == 0)
                {
                    e.Handled = true;
                }
                else
                {
                    _curLine.Remove(_curLine.Length - 1, 1);
                }
            }
            else if ( e.KeyChar == '\t' )
            {
                MoveToEnd( );
                e.Handled = true;
            }
            else if (char.IsLetterOrDigit(e.KeyChar) ||
              char.IsPunctuation(e.KeyChar) ||
              char.IsSeparator(e.KeyChar) ||
              char.IsSymbol(e.KeyChar) ||
              char.IsWhiteSpace(e.KeyChar))
            {
                MoveToEnd();
                _curLine.Append(e.KeyChar);
            }
            else if (e.KeyChar == 22)
            {
                //paste
                MoveToEnd();
                _curLine.Append(Clipboard.GetText());
            }
        }

        private void MoveToEnd()
        {
            textBox1.SelectionStart = textBox1.Text.Length;
        }

        private void Form1_ResizeEnd(object sender, EventArgs e)
        {
            SetLocation();
        }

        private void Form1_Deactivate(object sender, EventArgs e)
        {
            Hide();
        }

        private void textBox1_KeyDown(object sender, KeyEventArgs e)
        {
            //38 == up
            //40 == down
            try
            {
                if (e.KeyCode == Keys.Up || e.KeyCode == Keys.Down)
                {
                    if (e.KeyCode == Keys.Up)
                        _commandBufferPosition--;
                    else if (e.KeyCode == Keys.Down)
                        _commandBufferPosition++;
                    if (_commandBufferPosition < 0)
                        _commandBufferPosition = 0;
                    else if (_commandBufferPosition > _commandBuffer.Count)
                        _commandBufferPosition = _commandBuffer.Count;
                    MoveToEnd();
                    string curline = _curLine.ToString();
                    _curLine = new StringBuilder();
                    //I hate this shit.  I wish it was a builder or stream
                    textBox1.Text = textBox1.Text.Substring(0, textBox1.Text.Length - curline.Length);
                    string newText = _commandBuffer[_commandBufferPosition-1] as string;
                    textBox1.AppendText(newText);
                    _curLine.Append(newText);
                }
                e.Handled = true;
            }
            catch (Exception)
            {
                //out of bounds, tough shit
            }
        }


		private void ParseCommand(string command)
		{
			List<string> args = new List<string>();
			string[] command_line = command.Split(new char[] { ' ', '\t', '\n', '\r' });

			foreach (string s in command_line)
				if (s.Length > 0)
					args.Add(s);

			if (args.Count > 0)
			{
				command = args[0].ToString();
				args.RemoveAt(0);

				if (actions.Contains(command))
					((Command)actions[command])(args.ToArray());
			}
		}

		private void CommandLs(string[] args)
		{
			//Dana: damn linux fanboys
			//nolan: hey im a linux fanboy...
			//miechu: me 2...

			try // check to see if cygwin ls is on the system
			{
				ProcessStartInfo si = new ProcessStartInfo("ls");
				si.WindowStyle = ProcessWindowStyle.Hidden;
				Process blah = Process.Start(si);
			}
			catch (Exception)
			{
				// if not just use dir
				_curLine = new StringBuilder("dir");
			}
		}

		private void CommandClearScreen(string[] args)
		{
			// clears the screen  
			textBox1.Clear();
			_commandBufferPosition = 1;
			_curLine = new StringBuilder();
		}

		private void CommandExit(string[] args)
		{
			Exiting(this, null);
			Environment.Exit(0);
		}

		private void CommandJediOpacity(string[] args)
		{
			try {
			  int o = Int32.Parse(args[0]);
			  if ( o > 35 && o <= 100 )
			  {
				  Opacity = ( double )o / 100;
			  }
			  else
			  {
				  textBox1.AppendText( "\r\nUsage: jediopacity # where # is between 36 and 100\r\n" );
			  }
			} catch (Exception) {
			  textBox1.AppendText( "\r\nUsage: jediopacity # where # is between 36 and 100\r\n" );
			}

			_curLine = new StringBuilder();
		}

		private void CommandPwd(string[] args)
		{
			_curLine = new StringBuilder("cd");
		}

    }

}